import { Injectable } from '@nestjs/common';
import { and, asc, eq, ilike, isNull } from 'drizzle-orm';
import { BaseService, db, sysDept, type SysDept, sysUser } from '@nbase/db';
import { BizException } from '@nbase/nest-mods';
import type { IDept, IDeptTree, IPaginationResponse } from '@nbase/shared';
import { CreateDeptDto } from './dto/create-dept.dto';
import { UpdateDeptDto } from './dto/update-dept.dto';
import { QueryDeptDto } from './dto/query-dept.dto';
import { toDept, toDeptPage } from './dept.mapper';

/**
 * 部门服务
 */
@Injectable()
export class DeptService extends BaseService<typeof sysDept> {
  constructor() {
    super(sysDept);
  }

  /**
   * 创建部门
   */
  async createDept(createDeptDto: CreateDeptDto): Promise<IDept> {
    // 检查 code 是否已存在
    const isExist = await this.exists(eq(sysDept.code, createDeptDto.code));
    if (isExist) {
      throw BizException.deptExist();
    }

    // 如果有父部门，检查是否存在
    if (createDeptDto.parentId) {
      const parent = await this.findById(createDeptDto.parentId);
      if (!parent) {
        throw BizException.deptNotFound('父部门不存在');
      }
    }

    const created = await this.create(createDeptDto);
    return toDept(created as SysDept);
  }

  /**
   * 更新部门
   */
  async updateDept(id: string, updateDeptDto: UpdateDeptDto): Promise<IDept> {
    // 如果更新 code，检查是否已存在
    if (updateDeptDto.code) {
      const existing = await this.findOne(eq(sysDept.code, updateDeptDto.code));
      if (existing && existing.id !== id) {
        throw BizException.deptExist();
      }
    }

    // 如果有父部门，检查是否存在
    if (updateDeptDto.parentId) {
      const parent = await this.findById(updateDeptDto.parentId);
      if (!parent) {
        throw BizException.deptNotFound('父部门不存在');
      }

      // 不能将部门设置为自己的子部门
      if (updateDeptDto.parentId === id) {
        throw BizException.invalidParams('不能将部门设置为自己的子部门');
      }
    }

    const updated = await this.update(id, updateDeptDto);
    return toDept(updated as SysDept);
  }

  /**
   * 删除部门
   */
  async deleteDept(id: string): Promise<void> {
    // 检查是否有子部门
    const hasChildren = await this.exists(eq(sysDept.parentId, id));
    if (hasChildren) {
      throw BizException.deptHasChildren();
    }

    // 检查部门下是否有用户
    const users = await db
      .select()
      .from(sysUser)
      .where(eq(sysUser.deptId, id))
      .limit(1);

    if (users.length > 0) {
      throw BizException.deptHasUsers();
    }

    await this.softDelete(id);
  }

  /**
   * 分页查询部门
   */
  async paginateDepts(query: QueryDeptDto): Promise<IPaginationResponse<IDept>> {
    const conditions = [];

    // 根据字典 ID 筛选
    if (query.code) {
      conditions.push(eq(sysDept.code, query.code));
    }
    if (query.name) {
      conditions.push(ilike(sysDept.name, `%${query.name}%`));
    }
    if (query.status !== undefined) {
      conditions.push(eq(sysDept.status, query.status));
    }

    const where = conditions.length > 0 ? and(...conditions) : undefined;
    const result = await this.paginate(query, where);
    return toDeptPage(result as IPaginationResponse<SysDept>);
  }

  /**
   * 获取部门树
   */
  async getDeptTree(): Promise<IDeptTree[]> {
    const depts = (await this.findAll()) as SysDept[];
    const plainDepts = depts.map(toDept);
    return this.buildTree(plainDepts, null);
  }

  /**
   * 获取根部门列表
   */
  async getRootDepts(): Promise<IDept[]> {
    const depts = await this.find(isNull(sysDept.parentId), {
      orderBy: asc(sysDept.sort),
    });
    return (depts as SysDept[]).map(toDept);
  }

  /**
   * 获取子部门列表
   */
  async getChildDepts(parentId: string): Promise<IDept[]> {
    const depts = await this.find(eq(sysDept.parentId, parentId), {
      orderBy: asc(sysDept.sort),
    });
    return (depts as SysDept[]).map(toDept);
  }

  /**
   * 根据 code 查找部门
   */
  async findByCode(code: string): Promise<IDept | null> {
    const dept = (await this.findOne(eq(sysDept.code, code))) as SysDept | undefined;
    return dept ? toDept(dept) : null;
  }

  /**
   * 构建树形结构
   */
  private buildTree(depts: IDept[], parentId: string | null): IDeptTree[] {
    return depts
      .filter((dept) => dept.parentId === parentId)
      .map((dept) => ({
        ...dept,
        children: this.buildTree(depts, dept.id),
      }));
  }
}

